// This is the main DLL file.

#include "stdafx.h"

#include "CuBlas.Net.h"


namespace CuBlasNet {
	int Blas::IndexOfMax(array<float>^ x, int startElement, int skipElements)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector must have at least one element.");

		pin_ptr<float> px = &x[startElement];
		float* npx = px;
		return cublasIsamax(x->Length, npx, skipElements) - 1;
	}

	int Blas::IndexOfMin(array<float>^ x, int startElement, int skipElements)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector must have at least one element.");

		pin_ptr<float> px = &x[startElement];
		float* npx = px;
		return cublasIsamin(x->Length, npx, skipElements) - 1;
	}

	float Blas::AbsValueSum(array<float>^ x, int startElement, int skipElements)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector must have at least one element.");

		pin_ptr<float> px = &x[startElement];
		float* npx = px;
		return cublasSasum(x->Length, npx, skipElements);
	}

	void Blas::Axpy(float alpha, array<float>^ x, int startElementX, int skipElementsX, array<float>^ y, int startElementY, int skipElementsY)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector 'x' must have at least one element.");
		if (y == nullptr) throw gcnew ArgumentNullException("y");
		if (y->Length == 0) throw gcnew ArgumentException("Vector 'y' must have at least one element.");
		if (x->Length != y->Length) throw gcnew ArgumentException("Vectors 'x' and 'y' must have the same number of elements.");

		pin_ptr<float> px = &x[startElementX];
		pin_ptr<float> py = &y[startElementY];
		float* npx = px;
		float* npy = py;

		cublasSaxpy(x->Length, alpha, npx, skipElementsX, npy, skipElementsY);
	}

	void Blas::Copy(array<float>^ x, int startElementX, int skipElementsX, array<float>^ y, int startElementY, int skipElementsY)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector 'x' must have at least one element.");
		if (y == nullptr) throw gcnew ArgumentNullException("y");
		if (y->Length == 0) throw gcnew ArgumentException("Vector 'y' must have at least one element.");
		if (x->Length != y->Length) throw gcnew ArgumentException("Vectors 'x' and 'y' must have the same number of elements.");

		pin_ptr<float> px = &x[startElementX];
		pin_ptr<float> py = &y[startElementY];
		float* npx = px;
		float* npy = py;

		cublasScopy(x->Length, npx, skipElementsX, npy, skipElementsY);
	}

	void Blas::Dot(array<float>^ x, int startElementX, int skipElementsX, array<float>^ y, int startElementY, int skipElementsY)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector 'x' must have at least one element.");
		if (y == nullptr) throw gcnew ArgumentNullException("y");
		if (y->Length == 0) throw gcnew ArgumentException("Vector 'y' must have at least one element.");
		if (x->Length != y->Length) throw gcnew ArgumentException("Vectors 'x' and 'y' must have the same number of elements.");

		pin_ptr<float> px = &x[startElementX];
		pin_ptr<float> py = &y[startElementY];
		float* npx = px;
		float* npy = py;

		cublasSdot(x->Length, npx, skipElementsX, npy, skipElementsY);
	}

	float Blas::Norm2(array<float>^ x, int startElement, int skipElements)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector must have at least one element.");

		pin_ptr<float> px = &x[startElement];
		float* npx = px;
		return cublasSnrm2(x->Length, npx, skipElements);
	}

	void Blas::Rotate(array<float>^ x, int startElementX, int skipElementsX, array<float>^ y, int startElementY, int skipElementsY, float cos, float sin)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector 'x' must have at least one element.");
		if (y == nullptr) throw gcnew ArgumentNullException("y");
		if (y->Length == 0) throw gcnew ArgumentException("Vector 'y' must have at least one element.");
		if (x->Length != y->Length) throw gcnew ArgumentException("Vectors 'x' and 'y' must have the same number of elements.");

		pin_ptr<float> px = &x[startElementX];
		pin_ptr<float> py = &y[startElementY];
		float* npx = px;
		float* npy = py;

		cublasSrot(x->Length, npx, skipElementsX, npy, skipElementsY, cos, sin);
	}

	void Blas::RotateG(float% sa, float% sb, [Out] float% sc, [Out] float% ss)
	{
		float nsa = sa;
		float nsb = sb;
		float nsc = sc;
		float nss = ss;

		cublasSrotg(&nsa, &nsb, &nsc, &nss);

		sa = nsa;
		sb = nsb;
		sc = nsc;
		ss = nss;
	}

	void Blas::RotateM(array<float>^ x, int startElementX, int skipElementsX, array<float>^ y, int startElementY, int skipElementsY, array<float>^ params)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector 'x' must have at least one element.");
		if (y == nullptr) throw gcnew ArgumentNullException("y");
		if (y->Length == 0) throw gcnew ArgumentException("Vector 'y' must have at least one element.");
		if (x->Length != y->Length) throw gcnew ArgumentException("Vectors 'x' and 'y' must have the same number of elements.");
		if (params->Length != 5) throw gcnew ArgumentException("Params must have 5 elements.");
		
		pin_ptr<float> px = &x[startElementX];
		pin_ptr<float> py = &y[startElementY];
		pin_ptr<float> pparams = &params[0];
		float* npx = px;
		float* npy = py;
		float* npparams = pparams;

		cublasSrotm(x->Length, npx, skipElementsX, npy, skipElementsY, npparams);
	}

	void Blas::RotateMG(float% sd1, float% sd2, float% sx1, float sy1, array<float>^% params)
	{
		if (params == nullptr) throw gcnew ArgumentNullException("x");
		if (params->Length != 5) throw gcnew ArgumentException("Params must have 5 elements.");

		pin_ptr<float> pparams = &params[0];
		float* npparams = pparams;

		float nsd1 = sd1;
		float nsd2 = sd2;
		float nsx1 = sx1;
		float nsy1 = sy1;

		cublasSrotmg(&nsd1, &nsd2, &nsx1, &nsy1, npparams);

		sd1 = nsd1;
		sd2 = nsd2;
		sx1 = nsx1;
	}

	void Blas::Scale(float alpha, array<float>^ x, int startElement, int skipElements)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector 'x' must have at least one element.");

		pin_ptr<float> px = &x[startElement];
		float* npx = px;

		cublasSscal(x->Length, alpha, npx, skipElements);
	}

	void Blas::Swap(array<float>^ x, int startElementX, int skipElementsX, array<float>^ y, int startElementY, int skipElementsY)
	{
		if (x == nullptr) throw gcnew ArgumentNullException("x");
		if (x->Length == 0) throw gcnew ArgumentException("Vector 'x' must have at least one element.");
		if (y == nullptr) throw gcnew ArgumentNullException("y");
		if (y->Length == 0) throw gcnew ArgumentException("Vector 'y' must have at least one element.");
		if (x->Length != y->Length) throw gcnew ArgumentException("Vectors 'x' and 'y' must have the same number of elements.");

		pin_ptr<float> px = &x[startElementX];
		pin_ptr<float> py = &y[startElementY];
		float* npx = px;
		float* npy = py;

		cublasSswap(x->Length, npx, skipElementsX, npy, skipElementsY);
	}
}